﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Threading;
using System.Collections.ObjectModel;
using System.Xml;
using System.Xml.XPath;

namespace XMM
{
    /// <summary>
    /// Interaktionslogik für Library.xaml
    /// </summary>
    public partial class Library : Window
    {
        /****************************
         * Global Variables
         * */
        XMMDBDataContext dbConnection = null;

        public bool IsDatabaseAvailable { get; set; }

        Mutex mutUpdateGUI = new Mutex();
        Mutex mutLoadSeries = new Mutex();
        Mutex mutLoadMovies = new Mutex();
        Mutex mutUpdateShowDash = new Mutex();
        Mutex mutUpdateMovieDash = new Mutex();
        Mutex mutUpdateWatchfolders = new Mutex();

        Settings oSettings = new Settings();

        public Objects.Audition SelectedAudition { get; set; }

        private bool bMoviesLoaded = false;
        private bool bSeriesLoaded = false;

        ObservableCollection<Objects.PlaybackHistoryRecord> _SelectedItemHistory = new ObservableCollection<Objects.PlaybackHistoryRecord>();

        public ObservableCollection<Objects.PlaybackHistoryRecord> SelectedItemHistory
        { get { return _SelectedItemHistory; } }

        private ObservableCollection<Objects.Watchfolder> _Watchfolders;
        public ObservableCollection<Objects.Watchfolder> Watchfolders { get { return _Watchfolders; } }

        public ObservableCollection<Objects.Series> UnseenShows { get; set; }
        public ObservableCollection<Objects.SeriesEpisode> UnseenEpisodes { get; set; }
        public ObservableCollection<Objects.Movie> UnseenMovies { get; set; }

        private Thread thLoadAuditions;

        public Objects.SeriesEpisode SelectedEpisode { get; set; }
        public Objects.Movie SelectedMovie { get; set; }

        public Objects.MediaInfo CurrentPlayback { get; set; }
        public bool IsPlaying { get; set; }

        private bool IsUpdatingAudiences { get; set; }

        /*************************************
         * INITIALIZATION
         * */
        public Library()
        {
            InitializeComponent();
            UnseenShows = new ObservableCollection<Objects.Series>();
            UnseenEpisodes = new ObservableCollection<Objects.SeriesEpisode>();
            UnseenMovies = new ObservableCollection<Objects.Movie>();
            _Watchfolders = new ObservableCollection<Objects.Watchfolder>();
        }

        /*****************************
         * Recoded Features from MainWindow.xaml
         * */
        /// <summary>
        /// Initialize Data.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Window_Loaded(object sender, RoutedEventArgs e)
        {
            RefreshAudiences();
            //RefreshXMM();

            // Check for updates if enabled...
            if (oSettings.CheckForUpdates)
            {
                TimeSpan oDifference = DateTime.Now.Subtract(oSettings.LastUpdate);

                if (oDifference.TotalDays > oSettings.CheckForUpdatesInterval)
                {
                    Objects.UpdateDescriptor oUpdateDescriptor = new Objects.UpdateDescriptor();
                }
            }
        }

        /// <summary>
        /// Start reloading of Audiences in separate thread.
        /// </summary>
        public void RefreshAudiences()
        {
            // Load Auditions.
            thLoadAuditions = new Thread(new ThreadStart(loadAuditions));
            thLoadAuditions.Start();
        }

        /// <summary>
        /// Start reloading of Watchfolders in separate thread.
        /// </summary>
        public void RefreshWatchfolders()
        {
            Thread thRefreshWatchfolders = new Thread(new ThreadStart(processWatchfolders));
            thRefreshWatchfolders.Start();
        }

        /// <summary>
        /// Reload Watchfolders.
        /// </summary>
        public void processWatchfolders()
        {
            if (mutUpdateGUI.WaitOne(1000))
            {
                Dispatcher.Invoke(new clearWatchfoldersCallback(clearWatchfolders));

                List<Objects.Watchfolder> lWatchfolders = Objects.Watchfolder.getAllWatchfolders();

                if (lWatchfolders != null && lWatchfolders.Count > 0)
                {
                    foreach (Objects.Watchfolder oWatchfolder in lWatchfolders)
                    {
                        Dispatcher.Invoke(new addWatchfolderCallback(addWatchfolder), new object[] { oWatchfolder });
                    }
                }

                Dispatcher.Invoke(new ReloadWatchfolderItemsCallback(ReloadWatchfolderItems));
                mutUpdateGUI.ReleaseMutex();
            }
        }

        /// <summary>
        /// Refresh Watchfolders-Items in lvWatchfolders.
        /// </summary>
        private void ReloadWatchfolderItems()
        {
            lvWatchfolders.Items.Refresh();
        }

        /// <summary>
        /// Refresh Watchfolders-Items in lvWatchfolders. (CALLBACK)
        /// </summary>
        private delegate void ReloadWatchfolderItemsCallback();

        /// <summary>
        /// Clear Watchfolders in lvWatchfolders.
        /// </summary>
        private void clearWatchfolders()
        {
            _Watchfolders = new ObservableCollection<Objects.Watchfolder>();
            lvWatchfolders.ItemsSource = Watchfolders;
            //lvWatchfolders.Items.Refresh();
        }

        /// <summary>
        /// Clear Watchfolders in lvWatchfolders. (CALLBACK)
        /// </summary>
        private delegate void clearWatchfoldersCallback();

        /// <summary>
        /// Add Watchfolder to _Watchfolders.
        /// </summary>
        /// <param name="oWatchfolder">(Objects.Watchfolder) Watchfolder to add to list.</param>
        /// <remarks>_Watchfolders is used a container for lvWatchfolders.ItemsSource</remarks>
        private void addWatchfolder(Objects.Watchfolder oWatchfolder)
        {
            _Watchfolders.Add(oWatchfolder);
        }

        /// <summary>
        /// Add Watchfolder to _Watchfolders. (CALLBACK)
        /// </summary>
        /// <param name="oWatchfolder">(Objects.Watchfolder) Watchfolder to add to list.</param>
        private delegate void addWatchfolderCallback(Objects.Watchfolder oWatchfolder);

        /// <summary>
        /// Reload contents.
        /// </summary>
        public void RefreshXMM()
        {
            // Reload Settings.
            oSettings = new Settings();

            // Clear Lists of unseen items.
            UnseenShows = new ObservableCollection<Objects.Series>();
            UnseenEpisodes = new ObservableCollection<Objects.SeriesEpisode>();

            // Update Connection-String and check connectivity.
            updateConnectionString();
            testConnection();

            // Load Movies.
            Thread thLoadMovies = new Thread(new ThreadStart(loadMovies));
            thLoadMovies.Start();

            // Load Series.
            Thread thLoadSeries = new Thread(new ThreadStart(loadSeries));
            thLoadSeries.Start();

            // Refresh Watchfolders.
            RefreshWatchfolders();
        }

        /// <summary>
        /// Reload contents. (CALLBACK)
        /// </summary>
        private delegate void RefreshXMMCallback();

        /// <summary>
        /// Clear loaded Shows.
        /// </summary>
        private void ClearSeries()
        {
            lvShows.Items.Clear();
        }

        /// <summary>
        /// Clear loaded Shows. (CALLBACK)
        /// </summary>
        private delegate void ClearSeriesCallback();

        /// <summary>
        /// Clear Playback-History.
        /// </summary>
        private void ClearHistory()
        {
            _SelectedItemHistory.Clear();
        }

        /// <summary>
        /// Clear Playback-History. (CALLBACK)
        /// </summary>
        private delegate void ClearHistoryCallback();

        /// <summary>
        /// Load TV-Shows.
        /// </summary>
        private void loadSeries()
        {
            if (mutLoadSeries.WaitOne(1000))
            {
                Dispatcher.Invoke(new updateStatusCallback(updateStatus), new object[] { "Lade Serien...", true });

                /***********************************
                    * Clear existing series
                    * */
                Dispatcher.Invoke(new ClearSeriesCallback(ClearSeries));
                Dispatcher.Invoke(new ClearHistoryCallback(ClearHistory));


                /***********************************
                * LOAD SERIES
                * */

                // Get all Shows.
                List<Objects.Series> lAvailableSeries = Objects.Series.getAllSeries();

                if (lAvailableSeries != null && lAvailableSeries.Count > 0)
                {
                    int iCurrentSeries = 1;
                    int iSeriesPercentage = (int)Math.Round((decimal)100 / lAvailableSeries.Count, 0);
                    int iCurrentPercentage = 0;

                    // Add each Show.
                    foreach (Objects.Series oSeries in lAvailableSeries)
                    {
                        Dispatcher.Invoke(new updateStatusCallback(updateStatus), new object[] { "Lade Serie " + iCurrentSeries.ToString() + " von " + lAvailableSeries.Count.ToString() + "...", true });

                        iCurrentPercentage += iSeriesPercentage;
                        if (iCurrentPercentage > 100)
                            iCurrentPercentage = 100;

                        //lvShows.Items.Add(lviShow);
                        Dispatcher.Invoke(new addShowCallback(addShow), new object[] { oSeries });
                        ++iCurrentSeries;
                    }
                }

                Dispatcher.Invoke(new updateStatusCallback(updateStatus), new object[] { "Bereit...", false });
                Dispatcher.Invoke(new updateShowDashboardCallback(updateShowDashboard));
                mutLoadSeries.ReleaseMutex();
            }
        }

        /// <summary>
        /// Refresh TV-Shows on Dashboard in a separate Thread.
        /// </summary>
        private void updateShowDashboard()
        {
            lblDashNewEpisodes.Content = "0";
            lvUnseenShows.Items.Clear();

            Thread thUpdateShowDashboard = new Thread(new ThreadStart(processDashboardShowUpdates));
            thUpdateShowDashboard.Start();
        }

        /// <summary>
        /// Refresh TV-Shows on Dashboard in a separate Thread. (CALLBACK)
        /// </summary>
        private delegate void updateShowDashboardCallback();

        /// <summary>
        /// Refresh TV-Shows on Dashboard.
        /// </summary>
        private void processDashboardShowUpdates()
        {
            if (SelectedAudition != null && SelectedAudition.AuditionID > 0)
            {
                Dispatcher.Invoke(new setDashNewEpisodesCallback(setDashNewEpisodes));

                if (UnseenShows.Count > 0)
                {
                    foreach (Objects.Series oShow in UnseenShows)
                    {
                        Dispatcher.Invoke(new addDashShowCallback(addDashShow), new object[] { oShow });
                    }
                }
            }
        }

        /// <summary>
        /// Add TV-Show to Dashboard.
        /// </summary>
        /// <param name="oShow">(Objects.Series) TV-Show to add.</param>
        private void addDashShow(Objects.Series oShow)
        {
            // Generate ListViewItem.
            ListViewItem lviShow = new ListViewItem();
            lviShow.Tag = oShow.SeriesID;

            // Set Layout for ListViewItem.
            Grid grdShowLayout = new Grid();
            RowDefinition rdPosterPart = new RowDefinition();
            RowDefinition rdTextPart = new RowDefinition();
            rdPosterPart.Height = new GridLength(30, GridUnitType.Auto);
            rdTextPart.Height = new GridLength(30, GridUnitType.Pixel);
            grdShowLayout.RowDefinitions.Add(rdPosterPart);
            grdShowLayout.RowDefinitions.Add(rdTextPart);

            // Load Banner.
            Image imgPoster = new Image();
            imgPoster.HorizontalAlignment = System.Windows.HorizontalAlignment.Stretch;
            //imgPoster.Width = 300;
            string sCoverPath = new Settings().LibraryDataStorage + "series\\images\\xmm_" + oShow.SeriesID.ToString() + ".jpg";
            if (System.IO.File.Exists(sCoverPath))
            {
                imgPoster.Source = new BitmapImage(new Uri(sCoverPath, UriKind.Absolute));
                imgPoster.MaxHeight = 60;
                imgPoster.Width = lvUnseenShows.ActualWidth - 10;
                imgPoster.Stretch = Stretch.Uniform;
            }
            else
            {
                imgPoster.Source = new BitmapImage(new Uri(Tools.getInstallationPath() + @"images\bin\nocover.png", UriKind.Absolute));
                imgPoster.Stretch = Stretch.Uniform;
            }
            imgPoster.VerticalAlignment = System.Windows.VerticalAlignment.Center;
            imgPoster.HorizontalAlignment = System.Windows.HorizontalAlignment.Left;

            // Add Details.
            Label lblTitle = new Label();
            lblTitle.Content = oShow.Name;
            lblTitle.FontSize = 14;
            lblTitle.FontWeight = FontWeights.Bold;
            lblTitle.VerticalAlignment = System.Windows.VerticalAlignment.Center;
            Grid.SetRow(lblTitle, 1);

            grdShowLayout.Children.Add(imgPoster);
            grdShowLayout.Children.Add(lblTitle);

            lviShow.Content = grdShowLayout;
            lvUnseenShows.Items.Add(lviShow);
        }

        /// <summary>
        /// Add TV-Show to Dashboard. (CALLBACK)
        /// </summary>
        /// <param name="oShow">(Objects.Series) TV-Show to add.</param>
        private delegate void addDashShowCallback(Objects.Series oShow);

        /// <summary>
        /// Update Unseen-Episode-Count on Dashboard.
        /// </summary>
        private void setDashNewEpisodes()
        {
            lblDashNewEpisodes.Content = UnseenEpisodes.Count.ToString() + " neue Folgen";
        }

        /// <summary>
        /// Update Unseen-Episode-Count on Dashboard. (CALLBACK)
        /// </summary>
        private delegate void setDashNewEpisodesCallback();

        /// <summary>
        /// Add Unseen Episode to List "UnseenEpisodes".
        /// </summary>
        /// <param name="oEpisode">(Objects.SeriesEpisode) Unseen Episode.</param>
        private void addUnseenEpisode(Objects.SeriesEpisode oEpisode)
        {
            UnseenEpisodes.Add(oEpisode);
        }

        /// <summary>
        /// Add Unseen Episode to List "UnseenEpisodes". (CALLBACK)
        /// </summary>
        /// <param name="oEpisode">(Objects.SeriesEpisode) Unseen Episode.</param>
        private delegate void addUnseenEpisodeCallback(Objects.SeriesEpisode oEpisode);

        /// <summary>
        /// Add Unseen TV-Show to List "UnseenShows".
        /// </summary>
        /// <param name="oShow">(Objects.Series) Unseen Show.</param>
        private void addUnseenShow(Objects.Series oShow)
        {
            UnseenShows.Add(oShow);
        }

        /// <summary>
        /// Add TV-Show to List of Shows on Shows-Tab.
        /// </summary>
        /// <param name="oShow">(Objects.Series) TV-Show to add.</param>
        private void addShow(Objects.Series oSeries)
        {
            /******************************
            * Build ListViewItem for current show
            * */
            ListViewItem lviShow = new ListViewItem();
            lviShow.Tag = oSeries.SeriesID;

            /******************************
             * Create ListViewItem-Layout
             * */
            Grid grdShowLayout = new Grid();
            grdShowLayout.HorizontalAlignment = System.Windows.HorizontalAlignment.Stretch;
            grdShowLayout.VerticalAlignment = System.Windows.VerticalAlignment.Center;

            RowDefinition rdPosterPart = new RowDefinition();
            RowDefinition rdTextPart = new RowDefinition();
            rdPosterPart.Height = new GridLength(60, GridUnitType.Pixel);
            rdTextPart.Height = new GridLength(26, GridUnitType.Pixel);
            grdShowLayout.RowDefinitions.Add(rdPosterPart);
            grdShowLayout.RowDefinitions.Add(rdTextPart);
            ColumnDefinition cdShowPart = new ColumnDefinition();
            ColumnDefinition cdSeenPart = new ColumnDefinition();
            cdShowPart.Width = new GridLength(80, GridUnitType.Star);
            cdSeenPart.Width = new GridLength(32, GridUnitType.Pixel);
            grdShowLayout.ColumnDefinitions.Add(cdShowPart);
            grdShowLayout.ColumnDefinitions.Add(cdSeenPart);

            // Load Poster or default image
            Image imgPoster = new Image();
            string sCoverPath = new Settings().LibraryDataStorage + "series\\images\\xmm_" + oSeries.SeriesID.ToString() + ".jpg";
            if (System.IO.File.Exists(sCoverPath))
            {
                imgPoster.Source = new BitmapImage(new Uri(sCoverPath, UriKind.Absolute));
                imgPoster.Stretch = Stretch.Uniform;
            }
            else
            {
                imgPoster.Source = new BitmapImage(new Uri(Tools.getInstallationPath() + @"images\bin\nocover.png", UriKind.Absolute));
                imgPoster.Stretch = Stretch.Uniform;
            }
            imgPoster.VerticalAlignment = System.Windows.VerticalAlignment.Center;
            imgPoster.HorizontalAlignment = System.Windows.HorizontalAlignment.Left;

            grdShowLayout.Children.Add(imgPoster);

            // Add Description
            Label lblShow = new Label();
            lblShow.Content = oSeries.Name;
            if (oSeries.Seasons.Count > 0)
                lblShow.Content += " (" + oSeries.Seasons.Count.ToString() + ")";

            grdShowLayout.Children.Add(lblShow);

            // Check if seen
            Image imgSeen = new Image();
            imgSeen.VerticalAlignment = System.Windows.VerticalAlignment.Top;

            int iUnseen = 0;

            if (SelectedAudition != null)
            {
                List<Objects.SeriesEpisode> lUnseenEpisodes = oSeries.GetUnseen(SelectedAudition.AuditionID);
                iUnseen = lUnseenEpisodes.Count;
                if (iUnseen > 0)
                {
                    foreach (Objects.SeriesEpisode oUnseenEpisode in lUnseenEpisodes)
                    {
                        addUnseenEpisode(oUnseenEpisode);
                    }
                }
                
            }
            else
                iUnseen = 0;

            if (iUnseen > 0)
            {
                imgSeen.Source = new BitmapImage(new Uri(Tools.getInstallationPath() + @"images\bin\new.png", UriKind.Absolute));
                Label lblCount = new Label();
                lblCount.Content = "(" + iUnseen.ToString() + ")";
                lblCount.VerticalAlignment = System.Windows.VerticalAlignment.Bottom;
                grdShowLayout.Children.Add(lblCount);
                Grid.SetColumn(lblCount, 1);

                addUnseenShow(oSeries);
            }

            grdShowLayout.Children.Add(imgSeen);

            // Set Coordinates within Grid
            Grid.SetRow(lblShow, 1);
            Grid.SetColumn(imgSeen, 1);
            Grid.SetRowSpan(imgSeen, 2);

            /******************************
             * Complete and add ListViewItem
             * */
            lviShow.Content = grdShowLayout;

            lvShows.Items.Add(lviShow);
        }

        /// <summary>
        /// Add TV-Show to List of Shows on Shows-Tab.
        /// </summary>
        /// <param name="oShow">(Objects.Series) TV-Show to add.</param>
        private delegate void addShowCallback(Objects.Series oSeries);

        /// <summary>
        /// Clear List of Movies on Movies-Tab.
        /// </summary>
        private void ClearMovies()
        {
            lvMovies.Items.Clear();
        }

        /// <summary>
        /// Clear List of Movies on Movies-Tab. (CALLBACK)
        /// </summary>
        private delegate void ClearMoviesCallback();

        /// <summary>
        /// Load Movies.
        /// </summary>
        private void loadMovies()
        {
            if (mutLoadMovies.WaitOne(1000))
            {
                Dispatcher.Invoke(new updateStatusCallback(updateStatus), new object[] { "Lade Filme...", true });

                Dispatcher.Invoke(new clearUnseenMoviesCallback(clearUnseenMovies));
                Dispatcher.Invoke(new ClearMoviesCallback(ClearMovies));
                Dispatcher.Invoke(new ClearHistoryCallback(ClearHistory));

                // Populate Movies-List.
                List<Objects.Movie> lMovies = Objects.Movie.getAllMovies(lblStatus, pbStatus);

                if (lMovies != null && lMovies.Count > 0)
                {
                    int iCurrentMovie = 1;
                    int iMoviePercentage = (int)Math.Round((decimal)100 / lMovies.Count, 0);
                    int iCurrentPercentage = 0;
                    foreach (Objects.Movie oMovie in lMovies)
                    {
                        Dispatcher.Invoke(new updateStatusCallback(updateStatus), new object[] { "Lade Film " + iCurrentMovie.ToString() + " von " + lMovies.Count.ToString() + "...", true });
                        iCurrentPercentage += iMoviePercentage;
                        if (iCurrentPercentage > 100)
                            iCurrentPercentage = 100;
                        Dispatcher.Invoke(new AddMovieCallback(AddMovie), new object[] { oMovie });
                        ++iCurrentMovie;
                    }

                    Dispatcher.Invoke(new updateDashboardMoviesCallback(updateDashboardMovies));
                }

                Dispatcher.Invoke(new updateStatusCallback(updateStatus), new object[] { "Bereit...", false });

                mutLoadMovies.ReleaseMutex();
            }
        }

        /// <summary>
        /// Clear List of unseen Movies.
        /// </summary>
        private void clearUnseenMovies()
        {
            UnseenMovies = new ObservableCollection<Objects.Movie>();
        }

        /// <summary>
        /// Clear List of unseen Movies. (CALLBACK)
        /// </summary>
        private delegate void clearUnseenMoviesCallback();

        /// <summary>
        /// Updated unseen Movies on Dashboard.
        /// </summary>
        private void updateDashboardMovies()
        {
            lblDashUnseenMovies.Content = UnseenMovies.Count + " neue Filme";
            lvUnseenMovies.Items.Clear();

            if (UnseenMovies != null && UnseenMovies.Count > 0)
            {
                foreach (Objects.Movie oMovie in UnseenMovies)
                {
                    ListViewItem lviMovie = new ListViewItem();
                    lviMovie.Tag = oMovie.MovieID;
                    lviMovie.Margin = new Thickness(0, 0, 0, 5);

                    Grid grdMovieLayout = new Grid();
                    ColumnDefinition cdPosterPart = new ColumnDefinition();
                    ColumnDefinition cdTextPart = new ColumnDefinition();
                    cdPosterPart.Width = new GridLength(55, GridUnitType.Pixel);
                    cdTextPart.Width = new GridLength(95, GridUnitType.Star);
                    grdMovieLayout.ColumnDefinitions.Add(cdPosterPart);
                    grdMovieLayout.ColumnDefinitions.Add(cdTextPart);

                    Image imgPoster = new Image();
                    string sCoverPath = oSettings.LibraryDataStorage + "movies\\thumb\\xmm_" + Tools.getFileName(oMovie.Files[0]) + ".jpg";
                    if(System.IO.File.Exists(sCoverPath))
                        imgPoster.Source = new BitmapImage(new Uri(sCoverPath, UriKind.Absolute));
                    else
                        imgPoster.Source = new BitmapImage(new Uri(Tools.getInstallationPath() + "images\\bin\\nocover.png"));

                    grdMovieLayout.Children.Add(imgPoster);
                    
                    Label lblTitle = new Label();
                    lblTitle.FontWeight = FontWeights.Bold;
                    lblTitle.Content = oMovie.Name;
                    lblTitle.HorizontalAlignment = System.Windows.HorizontalAlignment.Stretch;
                    lblTitle.HorizontalContentAlignment = System.Windows.HorizontalAlignment.Center;
                    lblTitle.FontSize = 14;

                    grdMovieLayout.Children.Add(lblTitle);
                    Grid.SetColumn(lblTitle, 1);

                    lviMovie.Content = grdMovieLayout;
                    lviMovie.MouseDoubleClick += new MouseButtonEventHandler(lviDashMovie_MouseDoubleClick);

                    lvUnseenMovies.Items.Add(lviMovie);
                }
            }
            else
                lblDashUnseenMovies.Content = "0 neue Filme";
        }

        /// <summary>
        /// Updated unseen Movies on Dashboard. (CALLBACK)
        /// </summary>
        private delegate void updateDashboardMoviesCallback();

        /// <summary>
        /// Play double-clicked Dashboard-Movie.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void lviDashMovie_MouseDoubleClick(object sender, MouseButtonEventArgs e)
        {
            if (lvUnseenMovies.SelectedIndex > -1)
            {
                Thread thPlaybackThread = new Thread(new ParameterizedThreadStart(startVLCPlayerMovie));
                thPlaybackThread.Start(new object[] { new Objects.Movie((int)((ListViewItem)lvUnseenMovies.SelectedItem).Tag), 0 });

                lvUnseenMovies.Items.RemoveAt(lvUnseenMovies.SelectedIndex);
            }
        }

        /// <summary>
        /// Add Movie to List of Movies on Movies-Tab.
        /// </summary>
        /// <param name="oMovie">(Objects.Movie) Movie to add.</param>
        private void AddMovie(Objects.Movie oMovie)
        {
            /******************************
             * Build ListViewItem for current movie
             * */
            ListViewItem lviMovie = new ListViewItem();
            lviMovie.Tag = oMovie.MovieID;
            lviMovie.Margin = new Thickness(0, 0, 0, 5);

            /******************************
             * Create ListeViewItem-Layout
             * */
            Grid grdMovieLayout = new Grid();
            grdMovieLayout.HorizontalAlignment = System.Windows.HorizontalAlignment.Stretch;
            grdMovieLayout.VerticalAlignment = System.Windows.VerticalAlignment.Center;
            ColumnDefinition cdTextPart = new ColumnDefinition();
            ColumnDefinition cdPosterPart = new ColumnDefinition();
            ColumnDefinition cdMarkerPart = new ColumnDefinition();
            cdPosterPart.Width = new GridLength(50, GridUnitType.Pixel);
            cdTextPart.Width = new GridLength(150, GridUnitType.Star);
            cdMarkerPart.Width = new GridLength(32, GridUnitType.Pixel);
            grdMovieLayout.ColumnDefinitions.Add(cdPosterPart);
            grdMovieLayout.ColumnDefinitions.Add(cdTextPart);
            grdMovieLayout.ColumnDefinitions.Add(cdMarkerPart);

            Image imgPoster = new Image();

            // Load Poster or default image
            string sCoverPath = new Settings().LibraryDataStorage + "movies\\thumb\\xmm_" + Tools.getFileName(oMovie.Files[0]) + ".jpg";
            if (System.IO.File.Exists(sCoverPath))
            {
                imgPoster.Source = new BitmapImage(new Uri(sCoverPath, UriKind.Absolute));
                imgPoster.Stretch = Stretch.Uniform;
            }
            else
            {
                imgPoster.Source = new BitmapImage(new Uri(Tools.getInstallationPath() + @"images\bin\nocover.png"));
            }
            imgPoster.VerticalAlignment = System.Windows.VerticalAlignment.Center;
            imgPoster.HorizontalAlignment = System.Windows.HorizontalAlignment.Left;

            grdMovieLayout.Children.Add(imgPoster);

            // Add Description
            Label lblMovie = new Label();
            lblMovie.Content = oMovie.Name;
            if (oMovie.Year > 1800)
                lblMovie.Content += " (" + oMovie.Year.ToString() + ")";

            grdMovieLayout.Children.Add(lblMovie);

            Image imgSeen = new Image();
            if (SelectedAudition != null && oMovie.SeenBy(SelectedAudition.AuditionID) == false)
            {
                imgSeen.Source = new BitmapImage(new Uri(Tools.getInstallationPath() + "images\\bin\\new.png"));
                UnseenMovies.Add(oMovie);
            }
            grdMovieLayout.Children.Add(imgSeen);
            
            Grid.SetColumn(lblMovie, 1);
            Grid.SetColumn(imgSeen, 2);

            /******************************
             * Complete and add ListViewItem
             * */
            lviMovie.Content = grdMovieLayout;
            lviMovie.MouseDoubleClick += new MouseButtonEventHandler(lviMovie_MouseDoubleClick);

            lvMovies.Items.Add(lviMovie);
        }

        /// <summary>
        /// Add Movie to List of Movies on Movies-Tab.
        /// </summary>
        /// <param name="oMovie">(Objects.Movie) Movie to add.</param>
        private delegate void AddMovieCallback(Objects.Movie oMovie);

        /// <summary>
        /// Play double-clicked Movie.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void lviMovie_MouseDoubleClick(object sender, MouseButtonEventArgs e)
        {
            Thread thPlaybackThread = new Thread(new ParameterizedThreadStart(startVLCPlayerMovie));
            thPlaybackThread.Start(new object[] { new Objects.Movie((int)((ListViewItem)lvMovies.SelectedItem).Tag), 0 });
            //startVLCPlayerMovie(new object[] { new Objects.Movie((int)((ListViewItem)lvMovies.SelectedItem).Tag), 0 });
        }

        /*****************************
         * Features copied from old MainWindow.xaml
         * */
        /// <summary>
        /// Settings-MenuItem clicked.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void mitSettings_Click(object sender, RoutedEventArgs e)
        {
            Dialoges.SetupSettings dlgSettings = new Dialoges.SetupSettings(this);
            dlgSettings.ShowDialog();
        }

        /// <summary>
        /// About-Dialog-MenuItem clicked.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void mitAbout_Click(object sender, RoutedEventArgs e)
        {
            About dlgAbout = new About();
            dlgAbout.ShowDialog();
        }

        /// <summary>
        /// Start Add-New-Folder-Dialog.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void mitAddFolder_Click(object sender, RoutedEventArgs e)
        {
            ImportMedia dlgMediaImporter = new ImportMedia(this);
            dlgMediaImporter.ShowDialog();
        }

        /// <summary>
        /// Exit-MenuItem clicked.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void mitExit_Click(object sender, RoutedEventArgs e)
        {
            Shutdown();
        }

        /// <summary>
        /// Shutdown all Playback-Activies, save progress, update DB and EXIT.
        /// </summary>
        /// <returns>[bool] true if shutdown is clean.</returns>
        public void Shutdown()
        {
            try
            {
                // NEED MORE CODE


                // EXIT
                Environment.Exit(0);
            }
            catch (Exception ex)
            {

            }
        }

        /// <summary>
        /// Show Playback-History-ToolbarItem clicked.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void tsbShowChronics_Click(object sender, RoutedEventArgs e)
        {
            if (SelectedAudition != null && SelectedAudition.AuditionID > 0)
            {
                Dialoges.Library.HistoryViewer dlgHistoryViewer = new Dialoges.Library.HistoryViewer(this);
                dlgHistoryViewer.ShowDialog();
            }
            else
            {
                MessageBox.Show("Bitte wählen Sie zuerst eine(n) ZuseherIn aus!", "Kein(e) ZuseherIn gewählt", MessageBoxButton.OK, MessageBoxImage.Warning);
            }
        }

        /// <summary>
        /// Manage-Audiences-ToolbarItem clicked.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnManageAudiences_Click(object sender, RoutedEventArgs e)
        {
            Dialoges.ManageAudiences dlgAudienceManager = new Dialoges.ManageAudiences(this);
            dlgAudienceManager.ShowDialog();
        }

        /// <summary>
        /// Load Audiences.
        /// </summary>
        private void loadAuditions()
        {
            if (mutUpdateGUI.WaitOne(1000))
            {
                this.Dispatcher.Invoke(new updateStatusCallback(updateStatus), new object[] { "Lade Zuseher...", true });
                this.Dispatcher.Invoke(new clearAuditionsCallback(clearAuditions));
                this.Dispatcher.Invoke(new clearDashboardCallback(clearDashboard));

                try
                {
                    List<Objects.Audition> lAuditions = Objects.Audition.getAllAuditions();

                    if (lAuditions != null && lAuditions.Count > 0)
                    {
                        foreach (Objects.Audition oAudition in lAuditions)
                        {
                            this.Dispatcher.Invoke(new addAuditionCallback(this.addAudition), new object[] { oAudition });
                        }
                    }

                    // Select last Audition if available.
                    if (oSettings.LastAuditionID != 0)
                    {
                        this.Dispatcher.Invoke(new selectAuditionCallback(this.selectAudition), new object[] { oSettings.LastAuditionID });
                    }

                    this.Dispatcher.Invoke(new updateStatusCallback(updateStatus), new object[] { "Bereit...", false });
                }
                catch (Exception ex)
                {
                    MessageBox.Show("Fehler beim Laden der Zuseher: " + ex.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
                }

                Dispatcher.Invoke(new RefreshXMMCallback(RefreshXMM));

                mutUpdateGUI.ReleaseMutex();
            }
        }

        /// <summary>
        /// Clear Dashboard.
        /// </summary>
        private void clearDashboard()
        {
            lvUnseenMovies.Items.Clear();
            lvUnseenShows.Items.Clear();
        }

        /// <summary>
        /// Clear Dashboard. (CALLBACK)
        /// </summary>
        private delegate void clearDashboardCallback();

        /// <summary>
        /// Set Dashboard-Visibilty.
        /// </summary>
        /// <param name="bNewVisibility">[true] to make Dashboard visible.
        /// [false] to hide Dashboard.</param>
        private void setDashboardVisibility(bool bNewVisibility)
        {
            if (bNewVisibility)
            {
                tabDashboard.Visibility = System.Windows.Visibility.Visible;
            }
            else
            {
                tabDashboard.Visibility = System.Windows.Visibility.Hidden;

                if (tabLibrary.SelectedIndex == 0)
                    tabLibrary.SelectedIndex = 1;
            }
        }

        /// <summary>
        /// Set Dashboard-Visibilty. (CALLBACK)
        /// </summary>
        /// <param name="bNewVisibility">[true] to make Dashboard visible.
        /// [false] to hide Dashboard.</param>
        private delegate void setDashboardVisibilityCallback(bool bNewVisibility);

        /// <summary>
        /// Code-Select an Audience in cmbAuditions.
        /// </summary>
        /// <param name="iAuditionID">(int) Target Audience-ID.</param>
        private void selectAudition(int iAuditionID)
        {
            clearDashboard();

            Objects.Audition oAudition = new XMM.Objects.Audition(iAuditionID);

            if (oAudition != null && oAudition.AuditionID != 0 && oAudition.AuditionID != 1)
            {
                cmbAuditionSelection.SelectedIndex = cmbAuditionSelection.Items.IndexOf(oAudition);
                cmbAuditionSelection.Text = oAudition.Name;
                SelectedAudition = oAudition;
                setDashboardVisibility(true);
            }
            else
            {
                setDashboardVisibility(false);
            }
        }

        /// <summary>
        /// Code-Select an Audience in cmbAuditionSelection. (CALLBACK)
        /// </summary>
        /// <param name="iAuditionID">(int) Target Audience-ID.</param>
        private delegate void selectAuditionCallback(int iAuditionID);

        /// <summary>
        /// Clear Audiences.
        /// </summary>
        private void clearAuditions()
        {
            cmbAuditionSelection.Items.Clear();
        }

        /// <summary>
        /// Clear Audiences. (CALLBACK)
        /// </summary>
        private delegate void clearAuditionsCallback();

        /// <summary>
        /// Add Audience to cmbAuditionSelection.
        /// </summary>
        /// <param name="oAudition">(Objects.Audition) new Audition.</param>
        private void addAudition(Objects.Audition oAudition)
        {
            cmbAuditionSelection.Items.Add(oAudition);
        }

        /// <summary>
        /// Add Audience to cmbAuditionSelection. (CALLBACK)
        /// </summary>
        /// <param name="oAudition">(Objects.Audition) new Audition.</param>
        private delegate void addAuditionCallback(Objects.Audition oAudition);

        /// <summary>
        /// User selected Audience from cmbAuditionSelection.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void cmbAuditionSelection_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            if (cmbAuditionSelection.SelectedIndex > -1)
            {
                Objects.Audition oAudition = (Objects.Audition)cmbAuditionSelection.SelectedItem;

                if (oAudition != null && oAudition.AuditionID > 1)
                    setDashboardVisibility(true);
                else
                    setDashboardVisibility(false);

                SelectedAudition = (Objects.Audition)cmbAuditionSelection.SelectedItem;
                oSettings.LastAuditionID = SelectedAudition.AuditionID;
                oSettings.Save();
                RefreshXMM();
            }
        }

        /// <summary>
        /// Update Statusbar.
        /// </summary>
        /// <param name="sNewStatusMessage">(string) Message to Display in Status-Bar and Head.</param>
        /// <param name="bEnableProgressAnimation">[true] to enable status-progress-marquee.
        /// [false] to disable status-progress-marquee.</param>
        public void updateStatus(string sNewStatusMessage, bool bEnableProgressAnimation)
        {
            this.Title = "XMM - " + sNewStatusMessage;
            lblStatus.Content = sNewStatusMessage;
            pbStatus.IsIndeterminate = bEnableProgressAnimation;
        }

        /// <summary>
        /// Update Statusbar. (CALLBACK)
        /// </summary>
        /// <param name="sNewStatusMessage">(string) Message to Display in Status-Bar and Head.</param>
        /// <param name="bEnableProgressAnimation">[true] to enable status-progress-marquee.
        /// [false] to disable status-progress-marquee.</param>
        public delegate void updateStatusCallback(string sNewStatusMessage, bool bEnableProgressAnimation);

        /// <summary>
        /// Update DB-Connection-String in app.config.
        /// </summary>
        /// <remarks>Changes required XMM-Restart to take effect.</remarks>
        public void updateConnectionString()
        {
            oSettings = new Settings();

            // Check if DB-File specified in settings.xml exists.
            if (oSettings.DatabaseStoragePath != "\\XMM.mdf")
            {
                if (System.IO.File.Exists(oSettings.DatabaseStoragePath) == false)
                {
                    MessageBox.Show("Die Datei \"" + oSettings.DatabaseStoragePath + "\" wurde nicht gefunden! Die Datenbank kann nicht geladen werden!", "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
                    return;
                }
            }
            else
            {
                if (System.IO.File.Exists(Tools.getInstallationPath() + "XMM.mdf") == false)
                {
                    MessageBox.Show("Die Datei \"" + Tools.getInstallationPath() + "XMM.mdf\" wurde nicht gefunden! Die Datenbank kann nicht geladen werden!", "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
                    return;
                }
            }

            /**********************************
             * Build new ConnectionString
             * */
            string sNewConnectionString = String.Empty;

            // Build ConnectionString according to location.
            if (oSettings.DatabaseStoragePath == "\\XMM.mdf")
                sNewConnectionString = @"Data Source=.\SQLEXPRESS;AttachDbFilename=|DataDirectory|" + oSettings.DatabaseStoragePath + @";Integrated Security=True;Connect Timeout=30;User Instance=True";
            else
                sNewConnectionString = @"Data Source=.\SQLEXPRESS;AttachDbFilename=" + oSettings.DatabaseStoragePath + @";Integrated Security=True;Connect Timeout=30;User Instance=True";

            try
            {
                // Open XMM.exe.Config as XML-File.
                XmlDocument xdApplicationConfig = new XmlDocument();
                xdApplicationConfig.Load(Tools.getInstallationPath() + "XMM.exe.config");

                // Load <connectionStrings>-Section.
                XmlNode xnConnectionStrings = xdApplicationConfig.DocumentElement.SelectSingleNode("//connectionStrings");

                // Remove existing connection-string.
                if (xnConnectionStrings != null && xnConnectionStrings.ChildNodes.Count > 0)
                {
                    //xnConnectionStrings.InnerXml.Remove(0);
                    xnConnectionStrings.RemoveAll();
                }

                // Build new Connection-String-Element (<add...>).
                XmlNode xnNewConnectionString = xdApplicationConfig.CreateNode(XmlNodeType.Element, "add", "");
                XmlAttribute xaName = xdApplicationConfig.CreateAttribute("name");
                xaName.InnerText = "XMM.Properties.Settings.XMMConnectionString";
                XmlAttribute xaNewConnectionStringProvider = xdApplicationConfig.CreateAttribute("providerName");
                xaNewConnectionStringProvider.InnerText = "System.Data.SqlClient";
                XmlAttribute xaNewConnectionStringValue = xdApplicationConfig.CreateAttribute("connectionString");
                xaNewConnectionStringValue.InnerText = sNewConnectionString;

                // Add Attributes to Element.
                xnNewConnectionString.Attributes.Append(xaName);
                xnNewConnectionString.Attributes.Append(xaNewConnectionStringProvider);
                xnNewConnectionString.Attributes.Append(xaNewConnectionStringValue);

                // Add new Connection-String to <connectionStrings>-Section
                //xnConnectionStrings.AppendChild(xnNewConnectionString);
                xnConnectionStrings.PrependChild(xnNewConnectionString);

                // Save changes to XMM.exe.config.
                xdApplicationConfig.Save(Tools.getInstallationPath() + "XMM.exe.config");

                System.Configuration.ConfigurationManager.RefreshSection("appSettings");
            }
            catch (Exception ex)
            {
                MessageBox.Show("Fehler beim Aktualisieren des Datenbank-Verbindungs-Strings: " + ex.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
            }
        }

        /// <summary>
        /// Test Connection-Availability.
        /// </summary>
        public void testConnection()
        {
            try
            {
                if (dbConnection == null)
                {
                    dbConnection = new XMMDBDataContext();
                }

                if (dbConnection.DatabaseExists())
                {
                    IsDatabaseAvailable = true;
                }
            }
            catch (System.Data.SqlClient.SqlException ex)
            {
                MessageBox.Show("Die Datenbank-Verbindung kann nicht hergestellt werden: " + ex.Message, "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
            }
        }

        /// <summary>
        /// Get Playback-History for a Movie and the Selected Audience.
        /// </summary>
        /// <param name="oSelectedMovie">(Objects.Movie) Target-Movie</param>
        private void getMovieHistory(Objects.Movie oSelectedMovie)
        {
            // Get Playback-History.
            _SelectedItemHistory = new ObservableCollection<Objects.PlaybackHistoryRecord>();
            List<Objects.PlaybackHistoryRecord> oPlaybackHistory = Objects.PlaybackHistoryRecord.getAllRecords(1, oSelectedMovie.MovieID);
            foreach (Objects.PlaybackHistoryRecord oRecord in oPlaybackHistory)
            {
                _SelectedItemHistory.Add(oRecord);
            }
            lvMovieHistory.ItemsSource = SelectedItemHistory;
            lvMovieHistory.Items.Refresh();
        }

        /// <summary>
        /// Get Playback-History for a Movie and the Selected Audience. (CALLBACK)
        /// </summary>
        /// <param name="oSelectedMovie">(Objects.Movie) Target-Movie</param>
        private delegate void getMovieHistoryCallback(Objects.Movie oSelectedMovie);

        /// <summary>
        /// User selected Movie in Movies-Tab.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void lvMovies_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            // Clear Item Details...
            //spnItemDetails.Children.Clear();
            grdMovieDetails.Children.Clear();
            //lvBrowser_left.Items.Clear();
            _SelectedItemHistory.Clear();

            if (((ListView)sender).SelectedIndex > -1)
            {
                Objects.Movie oSelectedMovie = new Objects.Movie((int)(((ListViewItem)((ListView)sender).SelectedItem).Tag));

                if (oSelectedMovie != null && oSelectedMovie.MovieID > 0)
                {
                    movieSelected(oSelectedMovie.MovieID);
                }

                getMovieHistory(oSelectedMovie);
            }
        }

        /// <summary>
        /// Load the selected movie into details-panel.
        /// </summary>
        /// <param name="iMovieID"></param>
        private void movieSelected(int iMovieID)
        {
            Controls.Library.Details_Movie ctlMovieDetails = new XMM.Controls.Library.Details_Movie(iMovieID);
            grdMovieDetails.Children.Clear();
            grdMovieDetails.Children.Add(ctlMovieDetails);
            ctlMovieDetails.HorizontalAlignment = HorizontalAlignment.Stretch;
            ctlMovieDetails.VerticalAlignment = VerticalAlignment.Stretch;
        }

        /// <summary>
        /// Play Movie in VLC.
        /// </summary>
        /// <param name="Data">(Object[]) [0] (Objects.Movie) Selected Movie.
        /// (Object[]) [1] (int) File-Count for this Movie.</param>
        public void startVLCPlayerMovie(Object Data)
        {
            Object[] Movie = (Object[])Data;

            Objects.Movie oMovie = (Objects.Movie)Movie[0];
            Objects.Genre oGenre = new Objects.Genre(oMovie.GenreID);
            int iFileCount = (int)Movie[1];

            if (oMovie.Files.Count < iFileCount)
                return;

            if (System.IO.File.Exists(oMovie.Files[iFileCount].Replace(";", "")) == false)
            {
                // Drop CurrentPlayback-Value & Playback-Status.
                CurrentPlayback = null;
                IsPlaying = false;
                MessageBox.Show("Die Datei \"" + oMovie.Files[iFileCount].Replace(";", "") + "\" wurde nicht gefunden!", "Medium nicht gefunden", MessageBoxButton.OK, MessageBoxImage.Error);
                return;
            }

            if (System.IO.File.Exists(oSettings.VLCExecutablePath) == false)
            {
                CurrentPlayback = null;
                IsPlaying = false;
                MessageBox.Show("Das angegebene Abspielprogram (\"" + oSettings.VLCExecutablePath + "\") wurde nicht gefunden! Bitte überprüfen Sie Ihre Einstellungen!", "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
                return;
            }

            Dispatcher.Invoke(new updateStatusCallback(updateStatus), new object[] { "Es läuft: " + oMovie.Name + " (" + oGenre.Name + ", " + oMovie.Year + ") [" + (iFileCount + 1).ToString() + "/" + oMovie.Files.Count.ToString() + "]", true });

            if (System.IO.File.Exists(oSettings.VLCExecutablePath))
            {
                System.Diagnostics.Process pVLCPlayer = new System.Diagnostics.Process();
                pVLCPlayer.StartInfo = new System.Diagnostics.ProcessStartInfo();
                pVLCPlayer.StartInfo.FileName = oSettings.VLCExecutablePath;
                pVLCPlayer.StartInfo.Arguments = "--fullscreen --play-and-exit \"" + oMovie.Files[iFileCount].Replace(";", "") + "\"";
                pVLCPlayer.Start();

                pVLCPlayer.WaitForExit();
            }
            else
            {
                MessageBox.Show("Die Video-Player-Anwendung wurde nicht in \"" + oSettings.VLCExecutablePath + "\" gefunden!", "Kein Video-Player gefunden", MessageBoxButton.OK, MessageBoxImage.Error);
            }

            // Check for additional files and play if any.
            if (iFileCount < (oMovie.Files.Count - 1))
            {
                ++iFileCount;
                startVLCPlayerMovie(new object[] { oMovie, iFileCount });
                return;
            }

            // Create playback record.
            Objects.PlaybackHistoryRecord.CreateRecord(SelectedAudition.AuditionID, 1, oMovie.MovieID);

            // Reload Playback-History.
            Dispatcher.Invoke(new getMovieHistoryCallback(getMovieHistory), new object[] { oMovie });
            //getMovieHistory(oMovie);

            // Update Audition.
            SelectedAudition.LastMediaID = oMovie.MovieID;
            SelectedAudition.LastMediaType = 1;
            SelectedAudition.SaveChanges();

            // Drop CurrentPlayback-Value & Playback-Status.
            CurrentPlayback = null;
            IsPlaying = false;

            // Toggle Status.
            this.Dispatcher.Invoke(new updateStatusCallback(updateStatus), new object[] { "Bereit...", false });
        }


        /*********************************************************
         * New Code
         * */
        /// <summary>
        /// User selected TV-Show in Shows-Tab.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void lvShows_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            if (lvShows.SelectedIndex > -1)
            {
                tcSeasons.Items.Clear();
                Objects.Series oSelectedShow = new Objects.Series((int)((ListViewItem)lvShows.SelectedItem).Tag);

                if (oSelectedShow != null && oSelectedShow.Seasons.Count > 0)
                {
                    int iCurrentTab = -1;
                    int iHighestTab = -1;

                    foreach (Objects.SeriesSeason oSeason in oSelectedShow.Seasons)
                    {
                        ++iCurrentTab;

                        TabItem tiSeason = new TabItem();
                        tiSeason.Header = oSeason.ToString();
                        if (oSeason.Episodes.Count > 0)
                        {
                            Controls.Library.Details_Season cSeasonDetails = new Controls.Library.Details_Season(this, oSeason);
                            tiSeason.Content = cSeasonDetails;
                            iHighestTab = iCurrentTab;
                        }
                        else
                        {
                            tiSeason.IsEnabled = false;
                        }
                        tcSeasons.Items.Add(tiSeason);
                    }

                    tcSeasons.SelectedIndex = iHighestTab;
                }
            }
            else
            {
                tcSeasons.Items.Clear();
            }
        }

        /// <summary>
        /// Get Playback-History for an Episode.
        /// </summary>
        /// <param name="oSelectedEpisode">(Objects.SeriesEpisode) Target-Episode.</param>
        public void getEpisodeHistory(Objects.SeriesEpisode oSelectedEpisode)
        {
            //ListBoxItem
            // Reset current Playback-History-List...
            _SelectedItemHistory.Clear();

            // Load Playback-History for all Audiences...
            List<Objects.PlaybackHistoryRecord> lPlaybackRecords = Objects.PlaybackHistoryRecord.getAllRecords(2, oSelectedEpisode.EpisodeID);
            if (lPlaybackRecords != null && lPlaybackRecords.Count > 0)
            {
                foreach (Objects.PlaybackHistoryRecord oRecord in lPlaybackRecords)
                {
                    _SelectedItemHistory.Add(oRecord);
                }
            }
            lvEpisodePlaybacks.ItemsSource = SelectedItemHistory;
            lvEpisodePlaybacks.Items.Refresh();
        }

        /// <summary>
        /// Create Playback-Record for the selected Episode and Audience.
        /// </summary>
        public void setEpisodeSeen()
        {
            if (SelectedEpisode != null)
            {
                /*
                ListViewItem lviSelectedItem = (ListViewItem)lvBrowser_left.SelectedItem;
                int iSelectedIndex = lvBrowser_left.SelectedIndex;
                lviSelectedItem.Foreground = Brushes.DarkGreen;
                lviSelectedItem.FontStyle = FontStyles.Italic;
                lviSelectedItem.Content = "√ " + lviSelectedItem.Content;

                //lvBrowser_left.Items[iSelectedIndex] = lviSelectedItem;
                lvBrowser_left.Items.RemoveAt(iSelectedIndex);
                lvBrowser_left.Items.Insert(iSelectedIndex, lviSelectedItem);

                lvBrowser_left.Items.Refresh();
                 * */
                
            }
        }

        /// <summary>
        /// User clicked SetShowSeen-MenuItem.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void mitSetShowSeen_Click(object sender, RoutedEventArgs e)
        {
            List<Objects.Series> lSelectedShows = new List<Objects.Series>();
            foreach (ListViewItem lviSelectedShow in lvShows.SelectedItems)
            {
                lSelectedShows.Add(new Objects.Series((int)lviSelectedShow.Tag));
            }

            Thread thSetShowSeenThread = new Thread(new ParameterizedThreadStart(SetShowSeen));
            thSetShowSeenThread.Start(lSelectedShows);
        }

        /// <summary>
        /// Create Playback-Records for all Episodes within the Target-Shows.
        /// </summary>
        /// <param name="oSelectedShows">(List(Objects.Series)) Target-Shows.</param>
        private void SetShowSeen(Object oSelectedShows)
        {
            List<Objects.Series> lSelectedItems = (List<Objects.Series>)oSelectedShows;

            if (lSelectedItems != null && lSelectedItems.Count > 0)
            {
                int iSuccessfully = 0;
                int iFailed = 0;

                foreach (Objects.Series oShow in lSelectedItems)
                {
                    this.Dispatcher.Invoke(new updateStatusCallback(updateStatus), new object[] { "Markiere alle Folgen von " + oShow.Name +" als gesehen...", true });

                    foreach (Objects.SeriesSeason oSeason in oShow.Seasons)
                    {
                        foreach (Objects.SeriesEpisode oEpisode in oSeason.Episodes)
                        {
                            this.Dispatcher.Invoke(new updateStatusCallback(updateStatus), new object[] { "Überprüfe \"" + oShow.Name + "\" S" + oSeason.Number.ToString() + " E" + oEpisode.EpisodeNo.ToString() + "...", true });
                            if (oEpisode.SeenBy(SelectedAudition.AuditionID) == false)
                            {
                                if (Objects.PlaybackHistoryRecord.CreateRecord(SelectedAudition.AuditionID, 2, oEpisode.EpisodeID))
                                    ++iSuccessfully;
                                else
                                    ++iFailed;
                            }
                        }
                    }
                }

                if (iFailed > 0)
                {
                    string sMessage = "Nicht alle Folgen konnten markiert werden:" + Environment.NewLine;
                    sMessage += "Erfolgreich:   " + iSuccessfully.ToString() + Environment.NewLine;
                    sMessage += "Fehlgeschlagen:" + iFailed.ToString();

                    MessageBox.Show(sMessage, "Nicht alle Folgen markiert", MessageBoxButton.OK, MessageBoxImage.Warning);
                }

                this.Dispatcher.Invoke(new updateStatusCallback(updateStatus), new object[] { "Bereit...", false });
                Dispatcher.Invoke(new RefreshXMMCallback(RefreshXMM));
            }
        }

        /// <summary>
        /// Create Playback-Records for all Episodes within the Target-Shows.
        /// </summary>
        /// <param name="oSelectedShows">(List(Objects.Series)) Target-Shows.</param>
        private delegate void SetShowSeenCallback();

        /// <summary>
        /// User clicked Delete-Show-MenuItem.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void mitDeleteShow_Click(object sender, RoutedEventArgs e)
        {
            if (lvShows.SelectedItems != null && lvShows.SelectedItems.Count > 0)
            {
                string sMessage = "Möchten Sie folgende Serien inkl. aller Staffeln und Folgen wirklich löschen? Der Vorgang kann nicht rückgängig gemacht werden!!" + Environment.NewLine + Environment.NewLine;

                foreach (ListViewItem lviShow in lvShows.SelectedItems)
                {
                    Objects.Series oShow = new Objects.Series((int)lviShow.Tag);
                    sMessage += oShow.Name + Environment.NewLine;
                }

                if (MessageBox.Show(sMessage, "Wirklich löschen", MessageBoxButton.YesNo, MessageBoxImage.Warning) == MessageBoxResult.Yes)
                {
                    int iSuccessfull = 0;
                    int iFailed = 0;

                    foreach (ListViewItem lviShow in lvShows.SelectedItems)
                    {
                        if (Objects.Series.Delete((int)lviShow.Tag))
                            ++iSuccessfull;
                        else
                            ++iFailed;
                    }

                    if (iFailed > 0)
                    {
                        sMessage = "Nicht alle Serien wurden gelöscht!" + Environment.NewLine;
                        sMessage += "Erfolgreich:    " + iSuccessfull.ToString() + Environment.NewLine;
                        sMessage += "Fehlgeschlagen: " + iFailed.ToString() + Environment.NewLine;
                        MessageBox.Show(sMessage, "Fehler beim Löschen", MessageBoxButton.OK, MessageBoxImage.Warning);
                    }

                    RefreshXMM();
                }
            }
        }

        /// <summary>
        /// Create Playback-Record for the selected Movie in Movies-Tab.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void mitSetMovieSeen_Click(object sender, RoutedEventArgs e)
        {
            if (lvMovies.SelectedItems != null && lvMovies.SelectedItems.Count > 0)
            {
                foreach (ListViewItem lviMovie in lvMovies.SelectedItems)
                {
                    Objects.Movie oMovie = new Objects.Movie((int)lviMovie.Tag);
                    Objects.PlaybackHistoryRecord.CreateRecord(SelectedAudition.AuditionID, 1, oMovie.MovieID);
                }

                RefreshXMM();
            }
        }

        /// <summary>
        /// Change driveletter for the selected shows in Shows-Tab.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void mitChangeShowDriveLetter_Click(object sender, RoutedEventArgs e)
        {
            if (lvShows.SelectedItems != null && lvShows.SelectedItems.Count > 0)
            {
                Dialoges.Library.ChangeDriveLetter dlgChangeDriveLetter = new Dialoges.Library.ChangeDriveLetter("");

                if (dlgChangeDriveLetter.ShowDialog() == true)
                {
                    foreach (ListViewItem lviShow in lvShows.SelectedItems)
                    {
                        Objects.Series oShow = new Objects.Series((int)lviShow.Tag);

                        foreach (Objects.SeriesSeason oSeason in oShow.Seasons)
                        {
                            if (oSeason.BaseDirectory != null && oSeason.BaseDirectory != String.Empty)
                            {
                                oSeason.BaseDirectory = dlgChangeDriveLetter.NewDriveLetter + oSeason.BaseDirectory.Remove(0, 1);
                                oSeason.SaveChanges();
                            }

                            foreach (Objects.SeriesEpisode oEpisode in oSeason.Episodes)
                            {
                                oEpisode.RelocateHDD(dlgChangeDriveLetter.NewDriveLetter);
                            }
                        }
                    }
                }
            }
        }

        /// <summary>
        /// Open File-Import-Dialog.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void mitAddFile_Click(object sender, RoutedEventArgs e)
        {
            Dialoges.Library.ImportFile dlgImportFile = new Dialoges.Library.ImportFile(this);
            dlgImportFile.ShowDialog();
        }

        /// <summary>
        /// Open Disc-Import-Dialog.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void mitAddDisc_Click(object sender, RoutedEventArgs e)
        {
            Dialoges.Library.ImportDisc dlgImportDisc = new Dialoges.Library.ImportDisc(this);
            dlgImportDisc.ShowDialog();
        }

        /// <summary>
        /// Check for updates.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void tsbCheckUpdates_Click(object sender, RoutedEventArgs e)
        {
            Objects.UpdateDescriptor oUpdateDescriptor = new Objects.UpdateDescriptor(true);
        }

        /// <summary>
        /// Change driveletter for the selected movies in Movies-Tab.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void mitChangeMovieDriveLetter_Click(object sender, RoutedEventArgs e)
        {
            if (lvMovies.SelectedItems != null && lvMovies.SelectedItems.Count > 0)
            {
                // Show Drive-Letter-Selector.
                Dialoges.Library.ChangeDriveLetter dlgDriveLetterSelector = new Dialoges.Library.ChangeDriveLetter("X");
                if (dlgDriveLetterSelector.ShowDialog() == true)
                {
                    int iSuccessCount = 0;
                    int iFailCount = 0;

                    // Update File-Strings for all selected movies.
                    foreach (ListViewItem lviMovie in lvMovies.SelectedItems)
                    {
                        Objects.Movie oMovie = new Objects.Movie((int)lviMovie.Tag);

                        List<string> lFiles = new List<string>();

                        int iCurrentFile = 0;

                        foreach (string sFile in oMovie.Files)
                        {
                            string sNewFile = dlgDriveLetterSelector.NewDriveLetter + sFile.Remove(0, 1);
                            lFiles.Add(sNewFile);

                            ++iCurrentFile;
                        }

                        oMovie.Files = lFiles;

                        if (oMovie.SaveChanges())
                            ++iSuccessCount;
                        else
                            ++iFailCount;
                    }

                    // Inform user if any movie failed.
                    if (iFailCount > 0)
                    {
                        string sMessage = "Nicht alle Filme konnten aktualisiert werden!" + Environment.NewLine + Environment.NewLine;
                        sMessage += "Erfolgreich:    " + iSuccessCount.ToString() + Environment.NewLine;
                        sMessage += "Fehlgeschlagen: " + iFailCount.ToString() + Environment.NewLine;

                        MessageBox.Show(sMessage, "Nicht alle Filme aktualisiert", MessageBoxButton.OK, MessageBoxImage.Warning);
                    }

                    // Refresh content if any item was updated.
                    if (iSuccessCount > 0)
                    {
                        //RefreshXMM();
                        //lvMovies_SelectionChanged(lvMovies.SelectedItem, null);
                        ListViewItem lviFirstSelected = (ListViewItem)lvMovies.SelectedItem;
                        movieSelected((int)lviFirstSelected.Tag);
                    }
                }
            }
        }

        /// <summary>
        /// Relocate files for the selected movies in Movies-Tab.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void mitRelocateMovieFiles_Click(object sender, RoutedEventArgs e)
        {
            throw new NotImplementedException();
        }

        /// <summary>
        /// Delete selected Movies in Movies-Tab.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void mitDeleteMovie_Click(object sender, RoutedEventArgs e)
        {
            if (lvMovies.SelectedItems != null && lvMovies.SelectedItems.Count > 0)
            {
                int iSuccessCount = 0;
                int iFailCount = 0;

                foreach (ListViewItem lviMovie in lvMovies.SelectedItems)
                {
                    Objects.Movie oMovie = new Objects.Movie((int)lviMovie.Tag);

                    if (oMovie.Delete())
                        ++iSuccessCount;
                    else
                        ++iFailCount;
                }

                if (iFailCount > 0)
                {
                    string sMessage = "Nicht alle Filme konnten gelöscht werden!" + Environment.NewLine + Environment.NewLine;
                    sMessage += "Erfolgreich:   " + iSuccessCount.ToString() + Environment.NewLine;
                    sMessage += "Fehlgeschlagen:" + iFailCount.ToString();

                    MessageBox.Show(sMessage, "Nicht alle Filme gelöscht", MessageBoxButton.OK, MessageBoxImage.Warning);
                }

                if (iSuccessCount > 0)
                    RefreshXMM();
            }
        }

        /// <summary>
        /// Amount of unseen Episodes within the currently selected unseen Show.
        /// </summary>
        private int CurrentUnseenShowEpisodes;

        /// <summary>
        /// Unseen Show selected on Dashboard.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void lvUnseenShows_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            if (tabDashboard.IsVisible)
            {
                SelectedUnseenShowIndex = lvUnseenShows.SelectedIndex;
                CurrentUnseenShowEpisodes = 0;
                lbUnseenEpisodes.Items.Clear();

                if (lvUnseenShows.SelectedIndex > -1)
                {
                    Objects.Series oShow = new Objects.Series((int)((ListViewItem)lvUnseenShows.SelectedItem).Tag);
                    foreach (Objects.SeriesEpisode oEpisode in UnseenEpisodes)
                    {
                        if (oEpisode.SeriesID == oShow.SeriesID)
                        {
                            Objects.SeriesSeason oSeason = new Objects.SeriesSeason(oEpisode.SeasonID);

                            ListBoxItem lbiEpisode = new ListBoxItem();
                            lbiEpisode.Tag = oEpisode.EpisodeID;
                            lbiEpisode.Content = "S" + oSeason.Number.ToString() + " E" + oEpisode.EpisodeNo.ToString() + " - " + oEpisode.Name;
                            if (System.IO.File.Exists(oEpisode.MediaFile) == false)
                                lbiEpisode.Foreground = Brushes.Red;

                            lbiEpisode.MouseDoubleClick += new MouseButtonEventHandler(lbiDashEpisode_MouseDoubleClick);

                            lbUnseenEpisodes.Items.Add(lbiEpisode);
                            ++CurrentUnseenShowEpisodes;
                        }
                    }
                }
            }
        }

        /// <summary>
        /// Currently selected unseen Show's Index on Dashboard.
        /// </summary>
        private int SelectedUnseenShowIndex;

        /// <summary>
        /// Play selected unseen Episode from Dashboard.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void lbiDashEpisode_MouseDoubleClick(object sender, MouseButtonEventArgs e)
        {
            if (lvUnseenShows.SelectedIndex > -1)
            {
                Objects.SeriesEpisode oEpisode = new Objects.SeriesEpisode((int)((ListBoxItem)lbUnseenEpisodes.SelectedItem).Tag);

                StartEpisodePlaybackThread(oEpisode);

                lbUnseenEpisodes.Items.Clear();
                UnseenEpisodes.Remove(oEpisode);
                --CurrentUnseenShowEpisodes;

                
                if (CurrentUnseenShowEpisodes == 0)
                    lvUnseenShows.Items.RemoveAt(lvUnseenShows.SelectedIndex);
                else
                {
                    lvUnseenShows.SelectedIndex = SelectedUnseenShowIndex;
                }
            }
        }

        /// <summary>
        /// Play Episode.
        /// </summary>
        /// <param name="oEpisode"></param>
        public void StartEpisodePlaybackThread(Objects.SeriesEpisode oEpisode)
        {
            Objects.SeriesSeason oSeason = new Objects.SeriesSeason(oEpisode.SeasonID);

            updateStatus("Es läuft: " + Objects.Series.getTitle(oEpisode.SeriesID) + " - S" + oSeason.Number.ToString() + " E" + oEpisode.EpisodeNo.ToString() + " - " + oEpisode.Name, true);

            CurrentPlayback = new Objects.MediaInfo();
            CurrentPlayback.FullPath = oEpisode.MediaFile;
            CurrentPlayback.UserdefinedMediaInfo = oEpisode;
            CurrentPlayback.MediaType = 1;

            ParameterizedThreadStart oThreadStart = new ParameterizedThreadStart(startVLCPlayerEpisode);
            Thread thEpisodePlayback = new Thread(oThreadStart);
            thEpisodePlayback.Start(oEpisode);
        }

        /// <summary>
        /// Play Episode in VLC.
        /// </summary>
        /// <param name="oEpisode">(Objects.SeriesEpisode) Target-Episode.</param>
        public void startVLCPlayerEpisode(Object oEpisode)
        {
            Settings oSettings = new Settings();
            oSettings.Load();

            Objects.SeriesEpisode Episode = (Objects.SeriesEpisode)oEpisode;

            if (System.IO.File.Exists(Episode.MediaFile) == false)
            {
                // Drop CurrentPlayback-Value & Playback-Status.
                CurrentPlayback = null;
                IsPlaying = false;
                MessageBox.Show("Die Datei \"" + Episode.MediaFile + "\" wurde nicht gefunden!", "Medium nicht gefunden", MessageBoxButton.OK, MessageBoxImage.Error);
                return;
            }

            if (System.IO.File.Exists(oSettings.VLCExecutablePath) == false)
            {
                CurrentPlayback = null;
                IsPlaying = false;
                MessageBox.Show("Das angegebene Abspielprogram (\"" + oSettings.VLCExecutablePath + "\") wurde nicht gefunden! Bitte überprüfen Sie Ihre Einstellungen!", "Fehler", MessageBoxButton.OK, MessageBoxImage.Error);
                return;
            }


            System.Diagnostics.Process pVLCPlayer = new System.Diagnostics.Process();
            pVLCPlayer.StartInfo = new System.Diagnostics.ProcessStartInfo();
            pVLCPlayer.StartInfo.FileName = oSettings.VLCExecutablePath;
            if (oSettings.VideoPlayerExtensions == 1)
            {
                pVLCPlayer.StartInfo.Arguments = "--fullscreen --play-and-exit ";
            }
            pVLCPlayer.StartInfo.Arguments += "\"" + Episode.MediaFile + "\"";
            pVLCPlayer.Start();

            pVLCPlayer.WaitForExit();

            // Create playback record.
            Objects.PlaybackHistoryRecord.CreateRecord(SelectedAudition.AuditionID, 2, Episode.EpisodeID);

            // Update Audition.
            SelectedAudition.LastMediaID = Episode.EpisodeID;
            SelectedAudition.LastMediaType = 2;
            SelectedAudition.SaveChanges();

            // Drop CurrentPlayback-Value & Playback-Status.
            CurrentPlayback = null;
            IsPlaying = false;

            // Toggle Status.
            this.Dispatcher.Invoke(new updateStatusCallback(updateStatus), new object[] { "Bereit...", false });
        }

        /// <summary>
        /// Open Folder-Import-Dialog for clicked Watchfolder.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void lvWatchfolders_MouseDoubleClick(object sender, MouseButtonEventArgs e)
        {
            if (lvWatchfolders.SelectedIndex > -1)
            {
                Objects.Watchfolder oWatchfolder = (Objects.Watchfolder)lvWatchfolders.SelectedItem;

                ImportMedia dlgImportMedia = new ImportMedia(this);
                dlgImportMedia.txtTargetFolder.Text = oWatchfolder.Path;
                dlgImportMedia.processFiles();
                dlgImportMedia.ShowDialog();
            }
        }

        /// <summary>
        /// Open Watchfolder-Manager-Dialog.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void mitManageWatchfolders_Click(object sender, RoutedEventArgs e)
        {
            Dialoges.ManageWatchfolders dlgManageWatchfolders = new Dialoges.ManageWatchfolders(this);
            dlgManageWatchfolders.ShowDialog();
        }

        /// <summary>
        /// Exit-Button on Toolbar clicked.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void tsbExit_Click(object sender, RoutedEventArgs e)
        {
            Shutdown();
        }

        private void tsbFeedMobileDevice_Click(object sender, RoutedEventArgs e)
        {
            int iSelectedMovies = lvUnseenMovies.SelectedItems.Count;
            int iSelectedShows = lvUnseenShows.SelectedItems.Count;

            if (iSelectedMovies + iSelectedShows > 0)
            {
                Dialoges.MobileDeviceFeeder dlgMobileDeviceFeeder = new Dialoges.MobileDeviceFeeder(this);
            }
            else
            {
                MessageBox.Show("Bitte wählen Sie zuerst Filme oder Serien aus!", "Kein Medien zu übertragen", MessageBoxButton.OK, MessageBoxImage.Warning);
            }
        }

    }
}
